/**身份证相关的功能*/
import dayjs from 'dayjs';
import { toJson } from './jsonFormat.mjs';

/** 性别 */
export enum sexValue {
  /** 男 */
  male = '1',
  /** 女 */
  female = '0',
  /** 其他 */
  other = '2',
}

/** 性别描述 */
export const sexDesc: Record<sexValue, '男' | '女' | '未知'> = {
  [sexValue.male]: '男',
  [sexValue.female]: '女',
  [sexValue.other]: '未知',
};

/**身份信息类 */
export class Identify {
  #value: string;

  constructor(id: string) {
    if (!validateId(id)) {
      throw new Error('身份证号格式错误');
    }
    this.#value = id;
  }

  /**生日 */
  /**
   * 获取身份证对应的生日日期
   * @returns 解析后的生日日期，如果解析失败则返回无效的 dayjs 对象
   */
  get _birthday(): dayjs.Dayjs {
    // 从身份证中提取 8 位生日信息
    const birthStr = this.value.slice(6, 14);
    // 解析生日信息
    const birthday = dayjs(birthStr, 'YYYYMMDD');
    // 检查解析结果是否有效
    return birthday.isValid() ? birthday : dayjs('');
  }

  /**
   * 获取身份证对应的生日日期字符串
   * @returns 格式化后的生日日期字符串，格式为 'yyyy-mm-dd'，如果解析失败则返回空字符串
   */
  get birthday() {
    if (!this._birthday.isValid()) {
      return '';
    }
    return this._birthday.format('YYYY-MM-DD');
  }

  /**
   * 获取身份证对应的年龄
   * @returns 计算后的年龄，如果计算失败则返回 0
   */
  get age() {
    const now = dayjs();
    const birth = this._birthday;
    if (!birth.isValid()) {
      return 0;
    }
    return now.diff(birth, 'year');
  }

  /**
   * 计算真实年龄
   * @returns 计算后的真实年龄，如果计算失败则返回 0
   */
  get realAge() {
    return dayjs().diff(this._birthday, 'year', true);
  }

  /**
   * 获取身份证对应的性别
   * @returns 性别枚举值，如果解析失败则返回 'other'
   */
  get sex() {
    const sex = this.value.slice(16, 17);
    return sex === sexValue.male
      ? sexValue.male
      : sex === sexValue.female
        ? sexValue.female
        : sexValue.other;
  }

  /**
   * 获取身份证对应的性别描述
   * @returns 性别描述字符串，例如 '男'、'女' 或 '未知'
   */
  get sexDesc() {
    return sexDesc[this.sex];
  }

  /**
   * 获取身份证对应的区县代码
   * @returns 区县代码字符串，例如 '110101'
   */
  get countyCode() {
    return this.value.slice(0, 6);
  }

  valueOf() {
    return this.#value;
  }

  get value() {
    return this.#value;
  }

  /**
   * 将身份证信息转换为 JSON 格式
   * @returns 包含身份证信息的 JSON 对象
   */
  toJson() {
    return toJson(this, [
      'value',
      'birthday',
      'age',
      'sex',
      'sexDesc',
      'countyCode',
    ]);
  }
}

/**
 * 生成身份证号
 * @param options 选项
 * @returns 身份证号
 */
export const generateId = (options: {
  /** 性别 */
  sex: sexValue.female | sexValue.male;
  /** 出生年份 */
  birthYear: number;
  /** 出生月份 */
  birthMonth: number;
  /** 出生日期 */
  birthDay: number;
  /** 区县代码 */
  countyCode: number;
  /** 数量 */
  count?: number;
  /** 是否乱序 */
  shuffle?: boolean;
}) => {
  const {
    sex,
    birthYear,
    birthMonth,
    birthDay,
    countyCode,
    count = 1,
    shuffle = false,
  } = options;

  // 验证区代码 - 支持1-6位数字，会自动前导零填充到6位
  if (
    !/^\d{1,6}$/.test(countyCode.toString()) ||
    countyCode < 1 ||
    countyCode > 999999
  ) {
    throw new Error('区县代码格式错误');
  }

  // 辅助函数：补零
  function pad(num: number, len: number): string {
    return num.toString().padStart(len, '0');
  }

  // 生成顺序码：3位，最后一位奇数为男，偶数为女
  function getOrderCode(index: number, sex: sexValue): string {
    let code: number;
    if (shuffle) {
      code = Math.floor(Math.random() * 1000);
    } else {
      const genderDigit = sex === sexValue.male ? 1 : 0; // 男奇，女偶
      code = index * 2 + genderDigit; // 简单递增，确保性别
      if (code > 999) code = genderDigit; // 循环
    }
    return pad(code, 3);
  }

  // 计算校验码
  function getCheckCode(id17: string): string {
    const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
    const codes = '10X98765432'.split('');
    let sum = 0;
    for (let i = 0; i < 17; i++) {
      sum += parseInt(id17[i]) * weights[i];
    }
    return codes[sum % 11];
  }

  const ids: string[] = [];
  const birth = pad(birthYear, 4) + pad(birthMonth, 2) + pad(birthDay, 2);
  const county = pad(countyCode, 6);
  const usedOrders = new Set<string>(); // 用于去重

  let attempt = 0;
  let generated = 0;

  while (generated < count && attempt < 1000) {
    // 防止无限循环，最多尝试1000次
    const order = getOrderCode(attempt, sex);
    const orderKey = county + birth + order;

    if (!usedOrders.has(orderKey)) {
      usedOrders.add(orderKey);
      const id17 = orderKey;
      const check = getCheckCode(id17);
      ids.push(id17 + check);
      generated++;
    }
    attempt++;
  }

  return count === 1 ? ids[0] : ids;
};

/**
 * 校验身份证号
 * @param id 身份证号
 * @returns 是否有效
 */
export const validateId = (id: string) => {
  if (!id || typeof id !== 'string') return false;
  if (!/^\d{17}[\dXx]$/.test(id)) return false;

  // 真实省份代码列表（前两位）
  const provinceCodes = [
    '11',
    '12',
    '13',
    '14',
    '15', // 北京 天津 河北 山西 内蒙古
    '21',
    '22',
    '23', // 辽宁 吉林 黑龙江
    '31',
    '32',
    '33',
    '34',
    '35',
    '36',
    '37', // 上海 江苏 浙江 安徽 福建 江西 山东
    '41',
    '42',
    '43',
    '44',
    '45',
    '46', // 河南 湖北 湖南 广东 广西 海南
    '50',
    '51',
    '52',
    '53',
    '54', // 重庆 四川 贵州 云南 西藏
    '61',
    '62',
    '63',
    '64',
    '65', // 陕西 甘肃 青海 宁夏 新疆
  ];
  const provinceCode = id.slice(0, 2);
  if (!provinceCodes.includes(provinceCode)) return false;

  const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
  const codes = '10X98765432';
  let sum = 0;
  for (let i = 0; i < 17; i++) {
    sum += parseInt(id[i]) * weights[i];
  }
  if (codes[sum % 11] !== id[17].toUpperCase()) return false;

  const birth = id.slice(6, 14);
  const year = parseInt(birth.slice(0, 4));
  const month = parseInt(birth.slice(4, 6));
  const day = parseInt(birth.slice(6, 8));
  if (year < 1900 || year > new Date().getFullYear()) return false;
  if (month < 1 || month > 12) return false;
  const date = new Date(year, month - 1, day);
  if (
    date.getFullYear() !== year ||
    date.getMonth() !== month - 1 ||
    date.getDate() !== day
  )
    return false;

  return true;
};
